package flca.voicenav;

import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;

import flca.android.util.LocationMgr;
import flca.android.util.SoundMgr;
import flca.voicenav.model.GpxRoute;
import flca.voicenav.model.VoiceNavData;
import flca.voicenav.util.ButtonMgr;
import flca.voicenav.util.DistHelper;
import flca.voicenav.util.GpxRouteHelper;
import flca.voicenav.util.GpxXmlParser;
import flca.voicenav.view.LocationOverlay;
import flca.voicenav.view.RouteOverlay;

public class VoiceNavActivity extends MapActivity implements LocationListener, OnInitListener,
		VoiceNavConstants
{
	private MapView mapView;
	private NavigateBackgroundTask mNavigateTask;

	private LocationMgr mLocationMgrHelper;
	private TextToSpeech mTts;
	private ButtonMgr mButtonMgr;
	private long mLastLocationUpdate = 0;
	private boolean firstTimes[] = new boolean[] {true, true};
	
	private static final int MY_LISTVIEW_REQCODE = 501;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);

		setContentView(R.layout.mapview);
		mapView = (MapView) findViewById(R.id.mapview);

		if (!VoiceNavData.isEndClicked()) {
			startSoundMgr();
			mTts = new TextToSpeech(this, this);
			
			mLocationMgrHelper = LocationMgr.initialize(this, new LocationListener[] {this, mNavigateTask} );
			mButtonMgr = new ButtonMgr(this);
		}
		
	}

	@Override
	protected void onStop() {
		super.onStop();
	}	

	@Override
	public void onDestroy()
	{
		if (mTts != null) {
			mTts.stop();
			mTts.shutdown();
		}

		super.onDestroy();
	}

	@Override
	protected void onPause() {
		super.onPause();
		
		// did we clicked Quit ourself
		if (isFinishing()) {
			
			if (mLocationMgrHelper != null) {
				mLocationMgrHelper.destroy();
			}
			
			stopService(new Intent(this, NavigateBackgroundTask.class));
		}
	}	


	@Override
	protected void onResume() {
		super.onResume();
		if (getRoute() != null) {
			drawRoute();
		} else {
			drawLocation();
		}
	}
	
	@Override
	public void onSaveInstanceState(Bundle savedInstanceState)
	{
		savedInstanceState.putBoolean("b1", firstTimes[0]);
		savedInstanceState.putBoolean("b2", firstTimes[1]);
		savedInstanceState.putLong("l1", mLastLocationUpdate);
//		savedInstanceState.putSerializable("loc", mLastLocation);
		mButtonMgr.saveState(savedInstanceState);
		VoiceNavData.saveState(savedInstanceState);
		super.onSaveInstanceState(savedInstanceState);
	}

	@Override
	public void onRestoreInstanceState(Bundle savedInstanceState)
	{
		super.onRestoreInstanceState(savedInstanceState);
		firstTimes[0] = savedInstanceState.getBoolean("b1");
		firstTimes[1] = savedInstanceState.getBoolean("b2");
//		mLastLocationUpdate = savedInstanceState.getLong("l1");
		savedInstanceState.getSerializable("loc");

		VoiceNavData.restoreState(savedInstanceState);
		
		if (mButtonMgr == null) {
			mButtonMgr = new ButtonMgr(this);
		}
		mButtonMgr.restoreState(savedInstanceState);
	}

	//--- end of lifecycle methods
	
	
	private void startSoundMgr()
	{
		SoundMgr.getInstance().initSounds(getBaseContext());
		SoundMgr.getInstance().addSound(SND_RINGTONE, R.raw.sound);
		SoundMgr.getInstance().addSound(SND_LOW_SIMPLE, R.raw.simplelow);
		SoundMgr.getInstance().addSound(SND_HIGH_SIMPLE, R.raw.simplehigh);
		SoundMgr.getInstance().addSound(SND_ICQ, R.raw.icq);
		SoundMgr.getInstance().addSound(SND_HAPPY, R.raw.positive);
		SoundMgr.getInstance().playSound(SND_RINGTONE);
	}

	private void drawRoute()
	{
		RouteOverlay route = new RouteOverlay(getGpxRoute());
		List<Overlay> overlays = mapView.getOverlays();
		overlays.clear();
		overlays.add(route);
		drawLocation();
	}

	private void drawLocation()
	{
		if (getCurrLocation() != null) {
			LocationOverlay locOverlay = new LocationOverlay(getCurrLocation());
			List<Overlay> overlays = mapView.getOverlays();
			if (overlays.size() > 1) {
				overlays.remove(1);
			}
			if (overlays.size() > 0 && !hasRouteOverlay(overlays)) {
				overlays.remove(0);				
			}
			overlays.add(locOverlay);
		}
	}
	
	private boolean hasRouteOverlay(List<Overlay> overlays) {
		return overlays.size() > 0 && 
		 	overlays.get(0).toString().indexOf(RouteOverlay.class.getSimpleName()) >= 0;
	}
	
	public void myClickHandler(View target)
	{
		switch (target.getId()) {
		case R.id.zoomLocation:
				zoomCurrentLocation();
			break;
		case R.id.zoomRoute:
			zoomEntireRoute();
		break;
		case R.id.load:
			Intent listIntent = new Intent(this, RouteListActivity.class);
			startActivityForResult(listIntent, MY_LISTVIEW_REQCODE);
			mButtonMgr.onLoadClicked(); // see onActivityResult for handling result
			break;
		case R.id.start:
			startBackgroundSpeechNavigation();
			mButtonMgr.onStartClicked();
			break;
		case R.id.end:
			onEndClicked();
			break;
		case R.id.info:
			onInfoClicked();
			break;
		default:
			break;
		}
	}

	private void onEndClicked()
	{
		new AlertDialog.Builder(this).
			setIcon(android.R.drawable.ic_dialog_alert).
			setTitle(R.string.quit).
			setMessage(R.string.really_quit)
			.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					finish();
				}

			}).setNegativeButton(R.string.no, null).show();
		
	}
	
	private void onInfoClicked() 
	{
		if (getCurrLocation() != null && getGpxRoute() != null) {
			drawLocation();
			long t1 = now();
			double d = DistHelper.calcMinDistance(geoPoint(getCurrLocation()), getGpxRoute());
			VoiceNavData.setInfoMsg("afstand tot route = " + ((int) d) + " (" + (now()-t1) + " msec)") ;
			Toast.makeText(this, VoiceNavData.getInfoMsg(), Toast.LENGTH_LONG).show();
		} else {
			Toast.makeText(this, "no location yet!!", Toast.LENGTH_LONG).show();
		}
	}
	
	private void startBackgroundSpeechNavigation()
	{
		Log.i(TAG, "starting SpeechBackgroundTask");
		try {
			Intent intent = new Intent(this, NavigateBackgroundTask.class);
			startService(intent);
		} catch(Throwable t) {
			System.out.println(t);
		}
	}

	private void zoomEntireRoute()
	{
		if (getCurrentLocation() != null) {
			if (getGpxRoute() != null) {
				MapController mc = mapView.getController();
				int span[] = getZoomSpanEntireRoute();
				mc.setCenter(getCentreEntireRoute(span));
				mc.zoomToSpan(span[0], span[1]);
			}
		}
	}

	private void zoomCurrentLocation()
	{
		MapController mc = mapView.getController();
		mc.setCenter(geoPoint(getCurrentLocation()));
		Log.i(TAG, "curloc" + getCurrentLocation().getLatitude() + "," + getCurrentLocation().getLongitude());
		mc.zoomToSpan(100, 100);
	}

	private Location getCurrentLocation() {
		return 	mLocationMgrHelper.getCurrentLocation();
	}
	
	private int[] getZoomSpanEntireRoute()
	{
		if (getCurrentLocation() != null) {
			int[] r = new int[4];

			GeoPoint p1 = geoPoint(getGpxRoute().getMinLat(), getGpxRoute().getMinLng());
			GeoPoint p2 = geoPoint(getGpxRoute().getMaxLat(), getGpxRoute().getMaxLng());
			GeoPoint loc = geoPoint(getCurrentLocation());
			
			int minx = (loc.getLatitudeE6() < p1.getLatitudeE6()) ? loc.getLatitudeE6() : p1.getLatitudeE6();
			int maxx = (loc.getLatitudeE6() > p2.getLatitudeE6()) ? loc.getLatitudeE6() : p2.getLatitudeE6();
			int miny = (loc.getLongitudeE6() < p1.getLongitudeE6()) ? loc.getLongitudeE6() : p1.getLongitudeE6();
			int maxy = (loc.getLongitudeE6() > p2.getLongitudeE6()) ? loc.getLongitudeE6() : p2.getLongitudeE6();
			
			r[0] = maxx - minx;
			r[1] = maxy - miny;
			r[2] = (int) ((minx + maxx) / 2.0);
			r[3] = (int) ((miny + maxy) / 2.0);
			
			return r;
		} else {
			return null;
		}
	}

	private GeoPoint getCentreEntireRoute(int span[])
	{
		return geoPoint(span[2] / 1E6, span[3] / 1E6);
	}

	
	@Override
	protected boolean isLocationDisplayed()
	{
		return true;
	}

	@Override
	protected boolean isRouteDisplayed()
	{
		return false;
	}

	protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		Log.i("CheckStartActivity", "onActivityResult and resultCode = " + resultCode);
		super.onActivityResult(requestCode, resultCode, data);

		if (requestCode == MY_LISTVIEW_REQCODE) {
			processSelectedRoute(resultCode, data);
		} 
	}

	private void processSelectedRoute(int resultCode, Intent data)
	{
		if (getRoute(data)) {
			drawRoute();
			zoomEntireRoute();
		}
	}

	private boolean getRoute(Intent data)
	{
		try {
			String url = data.getStringExtra(SELC_FNAME);
			if (url != null) {
				long now = new Date().getTime();
				Log.i(TAG, "staring get route " + url);
				VoiceNavData.setGpxRoute(new GpxXmlParser().getRoute(new URL(url)));
				Log.i(TAG, "parsed route in " + (new Date().getTime() - now));
				new GpxRouteHelper(getRoute()).calcValues();
				Log.i(TAG, "finished get route in " + (new Date().getTime() - now) + " " + getRoute().size() + " nodes");
				return true;
			}
		} catch (Exception e) {
			Log.e(TAG, "" + e);
		}
		return false;
	}

	
	//--- location events
	@Override
	public void onLocationChanged(Location aLoc)
	{
		if (new Date().getTime() - mLastLocationUpdate > 10000) {
			mLastLocationUpdate = new Date().getTime();
			drawLocation();
			processLocation(aLoc);
		}
	}

	private void processLocation(Location aLoc)
	{
		if (getGpxRoute() != null && !VoiceNavData.isStartClicked()) {
			double d = DistHelper.calcMinDistance(geoPoint(aLoc), getGpxRoute());
			VoiceNavData.setInfoMsg("afstand tot route = " + ((int) d)) ;

			if (d < MIN_NAVPNT_DIST) {
				mButtonMgr.routeFound();
				if (firstTimes[0]) {
					firstTimes[0] = false;
					SoundMgr.getInstance().playSound(SND_RINGTONE);
				}
			}
		}
	}
	
	@Override
	public void onProviderDisabled(String arg0)
	{
	}

	@Override
	public void onProviderEnabled(String arg0)
	{
	}

	@Override
	public void onStatusChanged(String arg0, int status, Bundle arg2)
	{
	}
	
	@Override
	public void onConfigurationChanged(Configuration newConfig)
	{
		if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
			// do your task
		} else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
			// do your task
		}
		super.onConfigurationChanged(newConfig);
	}

	//----------- shortcuts ------------
	private GeoPoint geoPoint(Location aLocation) {
		return geoPoint(aLocation.getLatitude(), aLocation.getLongitude());
	}
	private GeoPoint geoPoint(double lat, double lon) {
		return new GeoPoint((int) (lat * 1E6), (int) (lon * 1E6));
	}
	private GpxRoute getGpxRoute() {
		return VoiceNavData.getGpxRoute();
	}
	private Location getCurrLocation() {
		return LocationMgr.getinstance().getCurrentLocation();
	}
	
	@Override
	public void onInit(int status)
	{
		if (status == TextToSpeech.SUCCESS && mTts != null) {
			mTts.setLanguage(Locale.UK);
			if (firstTimes[1]) {
				firstTimes[1] = false;
				mTts.speak("speech activated", TextToSpeech.QUEUE_FLUSH, null);
			}
		}		
	}

	//---- shortcuts
	
	private GpxRoute getRoute() {
		return VoiceNavData.getGpxRoute(); 
	}
	private long now() {
		return new Date().getTime();
	}
}
